package au.edu.qut.bpm.genbinder.genetic.genesis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.Case;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;
import au.edu.qut.bpm.genbinder.models.Resource;

public class LogGreedyFactory extends LogAbstractFactory {
	
	public LogGreedyFactory(ConcreteEventLog log) {
		super(log);
	}

	public ConcreteEventLog generateRandomCandidate(boolean performSafetyCheck, Random rng) {
		ConcreteEventLog newLog = new ConcreteEventLog(templateLog);
		Set<Activity> scheduledActivities = new HashSet<Activity>();
		ArrayList<Case> alCase = new ArrayList<Case>(newLog.getAbstractLog().getCases());
		
		Collections.shuffle(alCase);
		
		for (Case c : alCase) {
			/*boolean first = true;
			long placeAfter = 0;
			if (first)
				placeAfter = c.getArrival();
			first = false;*/
			long placeAfter = c.getArrival();
			for (Activity a : c.getOrderedTrace()) {
				// Find the smallest start time
				Set<Resource> usedAtProposal = null;
				// Start with possible minimum

				//usedAtProposal = getRandomResourceSet(newLog, a, rng);
				///*
				if (performSafetyCheck) {
					usedAtProposal = getRandomPossibleResourceSetForProposedStartTime(newLog, a, placeAfter, rng);
				} else {
					usedAtProposal = getRandomResourceSet(newLog, a, rng);
				}
				//*/
				if (usedAtProposal == null) {
					boolean scheduled = false;
					// Use already scheduled activities to discretize problem
					for (Activity oa : scheduledActivities) {
						if (newLog.getActivityEnd(oa) >= placeAfter) {
							usedAtProposal = getRandomPossibleResourceSetForProposedStartTime(newLog, a, newLog.getActivityEnd(oa), rng);
							
							if (usedAtProposal != null) {
								newLog.setActivityStart(a, newLog.getActivityEnd(oa));
								newLog.setActivityResources(a, usedAtProposal);
								scheduled = true;
								break;
							}
						}
					}
					if (!scheduled)
						System.err.println("An activity could not be greedily scheduled: you should panic");
				} else {
					// Good news
					newLog.setActivityStart(a, placeAfter);
					newLog.setActivityResources(a, usedAtProposal);
				}
				placeAfter = newLog.getActivityEnd(a);
				scheduledActivities.add(a);
			}
		}
		return newLog;
	}

	@Override
	public ConcreteEventLog generateRandomCandidate(Random rng) {
		return generateRandomCandidate(true, rng);
	}

	@Override
	public ConcreteEventLog generateRandomCandidate() {
		return generateRandomCandidate(new Random());
	}
}
